{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from skimage.filters import gabor_kernel\n",
    "import cv2\n",
    "\n",
    "class KernelParams:\n",
    "    \"\"\"\n",
    "    A simple class to represent the parameters of a given Gabor kernel.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, wavelength: float, orientation: float):\n",
    "        \"\"\"Class constructor. Define the parameters of a Gabor kernel.\n",
    "\n",
    "        Args:\n",
    "            wavelength (float): Wavelength (in pixels) of a Gabor kernel.\n",
    "            orientation (float): Orientations (in radians) of a Gabor kernel.\n",
    "        \"\"\"      \n",
    "\n",
    "        self.wavelength = wavelength\n",
    "        \"\"\"Wavelength (in pixels) of a Gabor kernel.\"\"\"\n",
    "\n",
    "        self.orientation = orientation\n",
    "        \"\"\"Orientation (in radians) of a Gabor kernel.\"\"\"\n",
    "\n",
    "    def __hash__(self) -> int:\n",
    "        \"\"\"Generates a hash value for this object instance.\n",
    "\n",
    "\n",
    "        Returns:\n",
    "            int: Hash value of this object.\n",
    "        \"\"\"\n",
    "        return hash((self.wavelength, self.orientation))\n",
    "\n",
    "    def __eq__(self, other: KernelParams) -> bool:\n",
    "        \"\"\"Verifies if this object instance is equal to another.\n",
    "        This method is the implementation of the == operator.\n",
    "\n",
    "        Args:\n",
    "            other (KernelParams):  Other instance to compare with this one.\n",
    "\n",
    "        Returns:\n",
    "            bool: True if this and the other instances have the same parameters, or\n",
    "            False otherwise.\n",
    "        \"\"\"      \n",
    "        return (self.wavelength, self.orientation) == \\\n",
    "               (other.wavelength, other.orientation)\n",
    "\n",
    "\n",
    "    def __ne__(self, other: KernelParams) -> bool:\n",
    "        \"\"\"Verifies if this object instance is different than another.\n",
    "        This method is the implementation of the != operator.\n",
    "\n",
    "        Args:\n",
    "            other (KernelParams): Other instance to compare with this one.\n",
    "\n",
    "\n",
    "        Returns:\n",
    "            bool: True if this and the other instances have different parameters, or\n",
    "            False otherwise.\n",
    "        \"\"\"\n",
    "        return not(self == other)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class GaborBank:\n",
    "    \"\"\"\n",
    "    Represents a bank of gabor kernels.\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, w = [4, 7, 10, 13],\n",
    "                       o = [i for i in np.arange(0, np.pi, np.pi / 8)]):\n",
    "        \"\"\"\n",
    "        Class constructor. Create a bank of Gabor kernels with a predefined set\n",
    "        of wavelengths and orientations.\n",
    "\n",
    "        The bank is composed of one kernel for each combination of wavelength x\n",
    "        orientation. For the rationale regarding the choice of parameters, refer\n",
    "        to the PhD thesis of the author of this code.\n",
    "        \"\"\"\n",
    "\n",
    "        self._wavelengths = w    \n",
    "        \"\"\"\n",
    "        List of wavelengths (in pixels) used to create the bank of Gabor\n",
    "        kernels.\n",
    "        \"\"\"\n",
    "\n",
    "        self._orientations = o\n",
    "        \"\"\"\n",
    "        List of orientations (in radians) used to create the bank of Gabor\n",
    "        kernels.\n",
    "        \"\"\"\n",
    "\n",
    "        self._kernels = {}\n",
    "        \"\"\"Dictionary holding the Gabor kernels in the bank.\"\"\"\n",
    "\n",
    "        # Create one kernel for each combination of wavelength x orientation\n",
    "        for wavelength in self._wavelengths:\n",
    "            for orientation in self._orientations:\n",
    "                # Convert wavelength to spatial frequency (scikit-image's\n",
    "                # interface expects spatial frequency, even though the original\n",
    "                # equation uses wavelengths - see https://en.wikipedia.org/wiki/\n",
    "                # Gabor_filter/)\n",
    "                frequency = 1 / wavelength\n",
    "\n",
    "                # Create and save the kernel\n",
    "                kernel = gabor_kernel(frequency, orientation)\n",
    "                par = KernelParams(wavelength, orientation)\n",
    "                self._kernels[par] = kernel\n",
    "\n",
    "    #---------------------------------------------\n",
    "    def filter(self, image: numpy.array) -> numpy.array:\n",
    "        \"\"\"\n",
    "        Filter the given image with the Gabor kernels in this bank.\n",
    "\n",
    "        Parameters\n",
    "        ----------\n",
    "        image: numpy.array\n",
    "            Image to be filtered.\n",
    "\n",
    "        Returns\n",
    "        -------\n",
    "        responses: numpy.array\n",
    "            List of the responses of the filtering with the Gabor kernels. The\n",
    "            responses are the magnitude of both the real and imaginary parts of\n",
    "            the convolution with each kernel, hence this list dimensions are the\n",
    "            same of the image, plus another dimension for the 32 responses (one\n",
    "            for each kernel in the bank, since there are 4 wavelengths and 8\n",
    "            orientations).\n",
    "        \"\"\"\n",
    "\n",
    "        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "        responses = []\n",
    "        for wavelength in self._wavelengths:\n",
    "            for orientation in self._orientations:\n",
    "\n",
    "                # Get the kernel\n",
    "                frequency = 1 / wavelength\n",
    "                par = KernelParams(wavelength, orientation)\n",
    "                kernel = self._kernels[par]\n",
    "\n",
    "                # Filter with both real and imaginary parts\n",
    "                real = cv2.filter2D(image, cv2.CV_32F, kernel.real)\n",
    "                imag = cv2.filter2D(image, cv2.CV_32F, kernel.imag)\n",
    "\n",
    "                # The response is the magnitude of the real and imaginary\n",
    "                # responses to the filters, normalized to [-1, 1]\n",
    "                mag = cv2.magnitude(real, imag)\n",
    "                cv2.normalize(mag, mag, -1, 1, cv2.NORM_MINMAX)\n",
    "\n",
    "                responses.append(mag)\n",
    "\n",
    "        return np.array(responses)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def GaborFilterFeature():\n",
    "    # Get the gabor filter\n",
    "    gabor = GaborBank()\n",
    "    imagePath = 'YourPicturePath'\n",
    "    image = Image.open(imagePath)  \n",
    "    image_np = np.array(image)  \n",
    "    responses = gabor.filter(image_np)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np  \n",
    "import matplotlib.pyplot as plt  \n",
    "from scipy import ndimage  # 正确导入ndimage模块  \n",
    "\n",
    "from skimage import data, filters, color  \n",
    "# 加载图像  \n",
    "image = data.astronaut()  # 使用scikit-image提供的示例图像  \n",
    "image = color.rgb2gray(image)  # 转换为灰度图像  \n",
    "  \n",
    "# Gabor滤波器参数  \n",
    "frequency = 0.2  # 频率，单位为像素的倒数  \n",
    "theta = np.pi / 4  # 方向  \n",
    "sigma_x = 5.0  # 高斯核x方向的标准差  \n",
    "sigma_y = 5.0  # 高斯核y方向的标准差  \n",
    "n_stds = 3  # Gabor滤波器核大小的标准差数量  \n",
    "offset = 0  # 相位偏移  \n",
    "  \n",
    "# 计算Gabor滤波器  \n",
    "gabor_kernel = filters.gabor_kernel(  \n",
    "    frequency, theta=theta, sigma_x=sigma_x, sigma_y=sigma_y, n_stds=n_stds, offset=offset  \n",
    ")  \n",
    "  \n",
    "# 提取Gabor滤波器的实部和虚部  \n",
    "gabor_kernel_real = np.real(gabor_kernel)  \n",
    "gabor_kernel_imag = np.imag(gabor_kernel)  \n",
    "  \n",
    "# 应用Gabor滤波器  \n",
    "filtered_real = ndimage.convolve(image, gabor_kernel_real, mode='constant')  \n",
    "filtered_imag = ndimage.convolve(image, gabor_kernel_imag, mode='constant')  \n",
    "  \n",
    "# 可视化结果  \n",
    "fig, axs = plt.subplots(1, 3, figsize=(12, 4))  \n",
    "# First Part \n",
    "axs[0].imshow(image, cmap='gray')  \n",
    "axs[0].set_title('Original Image')  \n",
    "axs[0].axis('off')  \n",
    "# Second Part\n",
    "axs[1].imshow(filtered_real, cmap='gray')  \n",
    "axs[1].set_title('Filtered Real Component')  \n",
    "axs[1].axis('off')  \n",
    "# Third Part\n",
    "axs[2].imshow(filtered_imag, cmap='gray')  \n",
    "axs[2].set_title('Filtered Imaginary Component')  \n",
    "axs[2].axis('off')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def relevantFeatures(gaborResponses :numpy.array, facialLandmarks:numpy.array) -> list:\n",
    "    \"\"\"Get the features that are relevant for the detection of emotions\n",
    "    from the matrix of responses to the bank of Gabor kernels.\n",
    "\n",
    "    The feature vector returned by this method can be used for training and\n",
    "    predicting, using a linear SVM.\n",
    "\n",
    "    Args:\n",
    "        gaborResponses (numpy.array): Matrix of responses to the bank of Gabor kernels applied to the face\n",
    "        region of an image. The first dimension of this matrix has size 32,\n",
    "        one for each kernel in the bank. The other two dimensions are in the\n",
    "        same size as the original image used for their extraction.\n",
    "        facialLandmarks (numpy.array): Bidimensional matrix with the coordinates of each facial landmark\n",
    "        detected in the face image from where the responses were obtained.\n",
    "\n",
    "    Returns:\n",
    "        list:A list with the responses of the 32 kernels at each of the\n",
    "        face landmarks.\n",
    "    \"\"\"  \n",
    "  \n",
    "    # Get the 32 responses at the positions of all the face landmarks\n",
    "    points = np.array(facialLandmarks)\n",
    "\n",
    "    # Try to get the responses for all points. If an exception is caught,\n",
    "    # it is because some landmarks are out of the image area (i.e. the face\n",
    "    # is partially occluded, but it was still possible to detect). In this\n",
    "    # case, assume 0.0 for the responses of the landmarks outside the image\n",
    "    # area.\n",
    "    try:\n",
    "        responses = gaborResponses[:, points[:, 1], points[:, 0]]\n",
    "    except:\n",
    "        w = gaborResponses.shape[2]\n",
    "        h = gaborResponses.shape[1]\n",
    "\n",
    "        responses = np.zeros((32, 68), dtype=float)\n",
    "        for i in range(len(points)):\n",
    "            x = points[i][0]\n",
    "            y = points[i][1]\n",
    "            if x < w and y < h:\n",
    "                responses[:, i] = gaborResponses[:, y, x]\n",
    "            else:\n",
    "                responses[:, i] = 0.0\n",
    "\n",
    "    # Reshape the bi-dimensional matrix to a single dimension\n",
    "    featureVector = responses.reshape(-1).tolist()\n",
    "\n",
    "    return featureVector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def getFeature(image):\n",
    "    \"\"\"\n",
    "    Detects a face and the prototypic emotions on the given frame image.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    image: numpy.ndarray\n",
    "        Image where to perform the detections from.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    ret: bool\n",
    "        Indication of success or failure.\n",
    "    \"\"\"\n",
    "    # 初始化检测器\n",
    "    try:  \n",
    "        faceDetector_test = FaceDetector()  \n",
    "    except Exception as e:  \n",
    "        print(f\"Something wrong: {e}\")\n",
    "    # Get the gabor filter\n",
    "    gabor = GaborBank()\n",
    "    ret, face = faceDetector_test.detect(image)\n",
    "    # Success find face\n",
    "    if ret:\n",
    "        # Crop just the face region\n",
    "        frame, face = face.crop(image)\n",
    "        # Filter it with the Gabor bank\n",
    "        responses = gabor.filter(frame)\n",
    "        # Filter only the responses at the facial landmarks\n",
    "        return relevantFeatures(responses, face.landmarks)\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_getFeature(imagePath):\n",
    "    # 读取JPEG图像  \n",
    "    image = Image.open(imagePath)  \n",
    "    # 将PIL图像转换为NumPy数组，以便与OpenCV兼容（如果需要）  \n",
    "    image_np = np.array(image)  \n",
    "    return getFeature(image_np)\n",
    "\n",
    "testImage_1 = 'YourImagePath'\n",
    "landMarkFature_1 = test_getFeature(testImage_1)\n",
    "print(\"Face Emotion Feature\", type(landMarkFature))\n",
    "print(len(landMarkFature))\n",
    "print(landMarkFature[:5])  # 打印前5个元素\n",
    "\n",
    "‍```\n",
    "Face detected: True\n",
    "Face Emotion Feature <class 'list'>\n",
    "2176\n",
    "[-0.72199946641922, 0.27436453104019165, -0.8671567440032959, 0.3090548813343048, -0.9074147939682007]\n",
    "Face Emotion Feature <class 'list'>\n",
    "2176\n",
    "[-0.72199946641922, 0.27436453104019165, -0.8671567440032959, 0.3090548813343048, -0.9074147939682007]\n",
    "‍```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Gabor滤波器处理后的内容不是图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def GaborFilterPicture():\n",
    "    # Get the gabor filter\n",
    "    gabor = GaborBank()\n",
    "    imagePath = '/root/MyCode/Mugulian_Sentiment_Analysis/model/Video/video_Sentiment_Detect/data/smell.jpeg'\n",
    "    # 读取JPEG图像  \n",
    "    image = Image.open(imagePath)  \n",
    "    # 将PIL图像转换为NumPy数组，以便与OpenCV兼容（如果需要）  \n",
    "    image_np = np.array(image)  \n",
    "    # Filter it with the Gabor bank\n",
    "    responses = gabor.filter(image_np)\n",
    "    # 将 NumPy 数组转换为 PIL 图像对象  \n",
    "    image_readyToSave = Image.fromarray(responses)  \n",
    "    # 显示 PIL 图像  \n",
    "    display(image_readyToSave)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
